home *** CD-ROM | disk | FTP | other *** search
/ Games of Daze / Infomagic - Games of Daze (Summer 1995) (Disc 1 of 2).iso / x2ftp / msdos / mxcode / tnypl211 / modplay.asm < prev    next >
Assembly Source File  |  1994-06-21  |  63KB  |  2,097 lines

  1. ;▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
  2. ;                Tiny MOD Player for Watcom C/C++32 and DOS/4GW
  3. ;                      Version 2.11a  June 5th, 1994
  4. ;
  5. ;                      Copyright 1993,94 Carlos Hasan
  6. ;▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
  7.  
  8. ideal
  9. p386
  10. model   flat,c
  11. smart
  12.  
  13. ;▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
  14. ; EQUATES AND PUBLICS
  15. ;▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
  16.  
  17. MAXVOICES = 8                           ; number of voices
  18. DMABUFLEN = 1024                        ; DMA buffer length (multiple of 64)
  19. VOLBUFLEN = 66*256                      ; volume table length
  20. MIXBUFLEN = 2*DMABUFLEN+2048            ; mixing/boosting buffer length
  21. TIMERRATE = 17000                       ; timer interrupt rate in ticks
  22.  
  23. global  MODPlayModule:proc
  24. global  MODStopModule:proc
  25. global  MODPlaySample:proc
  26. global  MODStopSample:proc
  27. global  MODSetPeriod:proc
  28. global  MODSetVolume:proc
  29. global  MODSetMusicVolume:proc
  30. global  MODSetSampleVolume:proc
  31. global  MODDetectCard:proc
  32. global  MODPoll:proc
  33. global  MODVoiceTable:dword
  34.  
  35. ;▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
  36. ; STRUCTURES
  37. ;▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
  38.  
  39. struc   module                          ; module structure
  40.   numtracks     dw      ?               ; number of tracks
  41.   orderlen      dw      ?               ; order length
  42.   orders        db      128 dup (?)     ; order list
  43.   patterns      dd      128 dup (?)     ; pattern addresses
  44.   sampptr       dd      32 dup (?)      ; sample start addresses
  45.   sampend       dd      32 dup (?)      ; sample end addresses
  46.   samploop      dd      32 dup (?)      ; sample loop point addresses
  47.   sampvolume    db      32 dup (?)      ; sample default volumes
  48. ends    module
  49.  
  50. struc   sample                          ; sample structure
  51.   period        dw      ?               ; default period
  52.   volume        dw      ?               ; default volume
  53.   datalen       dd      ?               ; sample data length
  54.   dataptr       dd      ?               ; sample data address
  55. ends    sample
  56.  
  57. struc   track                           ; track structure
  58.   note          dw      ?               ; note index
  59.   period        dw      ?               ; period value
  60.   inst          db      ?               ; instrument
  61.   volume        db      ?               ; volume
  62.   effect        dw      ?               ; effect
  63.   destperiod    dw      ?               ; toneporta wanted period
  64.   tonespeed     db      ?               ; toneporta speed
  65.   vibparm       db      ?               ; vibrato depth/rate
  66.   vibpos        db      ?               ; vibrato wave position
  67.   tremparm      db      ?               ; tremolo depth/rate
  68.   trempos       db      ?               ; tremolo wave position
  69.                 db      ?               ; alignment
  70.   arptable      dw      3 dup (?)       ; arpeggio periods
  71. ends    track
  72.  
  73. ;▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
  74. ; DATA
  75. ;▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
  76.  
  77. ;░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒
  78. ; Module Player data
  79. ;░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒
  80. udataseg
  81.  
  82. moduleptr       dd      ?               ; current module address
  83. pattptr         dd      ?               ; current playing pattern address
  84. orderpos        db      ?               ; order position
  85. orderlen        db      ?               ; order length
  86. pattrow         db      ?               ; pattern row
  87. tempo           db      ?               ; tempo
  88. tempocount      db      ?               ; tempo counter
  89. bpm             db      ?               ; beats per minute
  90. musicvolume     db      ?               ; music channels volume
  91. samplevolume    db      ?               ; sample channels volume
  92. numtracks       dw      ?               ; number of tracks
  93. tracks          track   MAXVOICES dup (?)
  94.  
  95. pitchtable      dd      3425 dup (?)    ; period to pitch table
  96.  
  97. ; Amiga period table
  98. dataseg
  99.  
  100. periodtable     dw      0
  101.                 dw      3424,3232,3048,2880,2712,2560,2416,2280,2152,2032,1920,1812
  102.                 dw      1712,1616,1524,1440,1356,1280,1208,1140,1076,1016,960,906
  103.                 dw      856,808,762,720,678,640,604,570,538,508,480,453
  104.                 dw      428,404,381,360,339,320,302,285,269,254,240,226
  105.                 dw      214,202,190,180,170,160,151,143,135,127,120,113
  106.                 dw      107,101,95,90,85,80,75,71,67,63,60,56
  107.                 dw      53,50,47,45,42,40,37,35,33,31,30,28
  108.  
  109. ; Sinus wave table
  110.  
  111. sintable        db      0,25,50,74,98,120,142,162,180,197,212,225
  112.                 db      236,244,250,254,255,254,250,244,236,225
  113.                 db      212,197,180,162,142,120,98,74,50,25
  114.  
  115. ;░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒
  116. ; Sound Blaster driver data
  117. ;░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒
  118. udataseg
  119.  
  120. ; Voices programmable parameters
  121.  
  122. label MODVoiceTable dword
  123.  
  124. voicepos        dd      MAXVOICES dup (?)
  125. voiceend        dd      MAXVOICES dup (?)
  126. voiceloop       dd      MAXVOICES dup (?)
  127. voicefrac       dd      MAXVOICES dup (?)
  128. voicepitch      dd      MAXVOICES dup (?)
  129. voicevolume     dd      MAXVOICES dup (?)
  130.  
  131. ; Internal driver data
  132. dataseg
  133.  
  134. mixbuffer       dd      ?               ; mixing buffer address
  135. boosttable      dd      ?               ; boosting table address
  136. voltable        dd      ?               ; volume table address
  137. numvoices       dw      ?               ; number of active voices
  138. mixfreq         dw      ?               ; playback frequency
  139. ioaddr          dw      ?               ; card I/O port address
  140. irqnum          db      ?               ; card IRQ level
  141. drqnum          db      ?               ; card DMA channel
  142. timerproc       dd      ?               ; timer callback address
  143. timeracc        dd      ?               ; timer callback accumulator
  144. timerspeed      dd      ?               ; timer callback speed
  145. datasel         dw      ?               ; flat model data selector
  146. bufsel          dw      ?               ; DOS memory block selector
  147. bufptr          dd      ?               ; DMA buffer address
  148. bufoff          dd      ?               ; double buffer offset
  149. oldirqoff       dd      ?               ; old IRQ vector address
  150. oldirqsel       dw      ?
  151. oldtimeroff     dd      ?               ; old timer IRQ0 vector address
  152. oldtimersel     dw      ?
  153. oldtimeracc     dw      ?               ; old timer accumulator
  154. manualmode      db      ?               ; timer/manual polling mode
  155. playing         db      0               ; playing/stopped status
  156.  
  157.  
  158. ;▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
  159. ; CODE
  160. ;▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
  161. codeseg
  162.  
  163. ;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
  164. ; Copyright Strings
  165. ;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
  166.  
  167. db      'Tiny MOD Player V2.11 Copyright 1993,94 Carlos Hasan',0
  168. db      'Compiled on: ',??date,' ',??time,0
  169.  
  170. ;░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒
  171. ; Module Player stuff
  172. ;░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒
  173.  
  174. ;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
  175. ; MODPlayModule - start playing a music module
  176. ; In:
  177. ;  Song  = module address
  178. ;  Chans = number of channels
  179. ;  Rate  = playback rate
  180. ;  Port  = port address
  181. ;  irq   = irq number
  182. ;  dma   = dma channel
  183. ;  mode  = polling mode
  184. ;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
  185. proc    MODPlayModule Song:dword,Chans:dword,Rate:dword,Port:dword,IRQ:dword,DRQ:dword,Mode:dword
  186.         pushad
  187.  
  188. ; setup the music module address
  189.  
  190.         mov     esi,[Song]
  191.         mov     [moduleptr],esi
  192.  
  193. ; setup the sound card driver
  194.  
  195.         mov     ax,[word Rate]
  196.         mov     bl,[byte Chans]
  197.         mov     dx,[word Port]
  198.         mov     cl,[byte IRQ]
  199.         mov     ch,[byte DRQ]
  200.         mov     bh,[byte Mode]
  201.         call    mixinit
  202.         jc      playmoduled0
  203.  
  204. ; build the period to pitch table (16.16 fixed point values)
  205.  
  206.         movzx   ebx,ax
  207.         mov     eax,8363*428
  208.         xor     edx,edx
  209.         shld    edx,eax,16
  210.         shl     eax,16
  211.         div     ebx
  212.         mov     esi,eax
  213.         lea     edi,[pitchtable]
  214.         mov     ecx,3425
  215.         xor     ebx,ebx
  216. playmodulel0:
  217.         inc     ebx
  218.         xor     edx,edx
  219.         mov     eax,esi
  220.         div     ebx
  221.         mov     [edi],eax
  222.         add     edi,4
  223.         loop    playmodulel0
  224.  
  225. ; setup global volumes for music and sample channels
  226.  
  227.         mov     [musicvolume],255
  228.         mov     [samplevolume],255
  229.  
  230. ; clear the module player track structures
  231.  
  232.         push    es
  233.         mov     ax,ds
  234.         mov     es,ax
  235.         cld
  236.         lea     edi,[tracks]
  237.         mov     ecx,MAXVOICES*(size track)
  238.         xor     al,al
  239.         rep     stosb
  240.         pop     es
  241.  
  242. ; check if there is a module to playback
  243.  
  244.         xor     eax,eax
  245.         mov     [numtracks],ax
  246.  
  247.         mov     esi,[moduleptr]
  248.         test    esi,esi
  249.         clc
  250.         je      playmoduled0
  251.  
  252. ; setup player interpreter variables
  253.  
  254.         mov     esi,[moduleptr]
  255.         mov     ax,[esi+module.orderlen]
  256.         mov     [orderlen],al
  257.         mov     ax,[esi+module.numtracks]
  258.         mov     [numtracks],ax
  259.         mov     [tempo],6
  260.         mov     [bpm],125
  261.         mov     [orderpos],0
  262.         mov     [tempocount],0
  263.         mov     [pattrow],40h
  264.  
  265. ; setup the player callback timer routine
  266.  
  267.         lea     edx,[pollmodule]
  268.         call    mixsettimerproc
  269.         mov     dl,[bpm]
  270.         call    mixstarttimer
  271.         clc
  272.  
  273. playmoduled0:
  274.         popad
  275.         sbb     eax,eax
  276.         ret
  277. endp    MODPlayModule
  278.  
  279. ;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
  280. ; MODStopModule - shut down the music system
  281. ;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
  282. proc    MODStopModule
  283.         pushad
  284.         call    mixstoptimer            ; stop the timer callback
  285.         call    mixdone                 ; shutdown the SB stuff
  286.         popad
  287.         ret
  288. endp    MODStopModule
  289.  
  290. ;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
  291. ; MODPoll - polls the music system in manual mode
  292. ;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
  293. proc    MODPoll
  294.         pushad
  295.         cmp     [manualmode],0          ; call the polling routine only
  296.         je      modpolld0               ; if we are using the manual
  297.         cmp     [playing],0             ; polling mode (and if the driver
  298.         je      modpolld0               ; is active).
  299.         call    mixpoll
  300. modpolld0:
  301.         popad
  302.         ret
  303. endp    MODPoll
  304.  
  305. ;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
  306. ; MODPlaySample - play sample instrument
  307. ; In:
  308. ;  voice  = voice number
  309. ;  sample = sample address
  310. ;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
  311. proc    MODPlaySample Voice:dword,SamplePtr:dword
  312.         pushad
  313.         cli
  314.  
  315. ; get the voice number and track address
  316.  
  317.         mov     ebx,[Voice]
  318.         mov     edi,[SamplePtr]
  319.         mov     esi,ebx
  320.         imul    esi,size track
  321.  
  322. ; set the voice pitch value
  323.  
  324.         movzx   eax,[edi+sample.period]
  325.         mov     eax,[4*eax+pitchtable]
  326.         mov     [4*ebx+voicepitch],eax
  327.  
  328. ; set the voice sample parameters
  329.  
  330.         mov     eax,[edi+sample.dataptr]
  331.         mov     [4*ebx+voicepos],eax
  332.         add     eax,[edi+sample.datalen]
  333.         mov     [4*ebx+voiceend],eax
  334.         mov     [4*ebx+voiceloop],eax
  335.  
  336. ; set the voice and track volumes
  337.  
  338.         mov     ax,[edi+sample.volume]
  339.         mov     [esi+tracks.volume],al
  340.         mul     [samplevolume]
  341.         mov     [byte 4*ebx+voicevolume],ah
  342.  
  343.         sti
  344.         popad
  345.         ret
  346. endp    MODPlaySample
  347.  
  348. ;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
  349. ; MODStopSample - stop the playing sample
  350. ; In:
  351. ;  voice = voice number
  352. ;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
  353. proc    MODStopSample Voice:dword
  354.         pushad
  355.         cli
  356.  
  357. ; get the voice number
  358.  
  359.         mov     ebx,[Voice]
  360.         xor     eax,eax
  361.  
  362. ; clear the voice sample parameters
  363.  
  364.         mov     [4*ebx+voicepos],eax
  365.         mov     [4*ebx+voiceend],eax
  366.         mov     [4*ebx+voiceloop],eax
  367.  
  368.         sti
  369.         popad
  370.         ret
  371. endp    MODStopSample
  372.  
  373. ;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
  374. ; MODSetPeriod -  set the voice period value
  375. ; In:
  376. ;  voice = voice number
  377. ;  period = period value (113-856)
  378. ;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
  379. proc    MODSetPeriod Voice:dword,Period:dword
  380.         pushad
  381.         cli
  382.  
  383. ; get the voice number and period value
  384.  
  385.         mov     ebx,[Voice]
  386.         mov     eax,[Period]
  387.  
  388. ; set the voice pitch value
  389.  
  390.         mov     eax,[4*eax+pitchtable]
  391.         mov     [4*ebx+voicepitch],eax
  392.  
  393.         sti
  394.         popad
  395.         ret
  396. endp    MODSetPeriod
  397.  
  398. ;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
  399. ; MODSetVolume -  set the voice volume level
  400. ; In:
  401. ;  voice = voice number
  402. ;  volume = volume level (0-64)
  403. ;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
  404. proc    MODSetVolume Voice:dword,Volume:dword
  405.         pushad
  406.         cli
  407.  
  408. ; get the voice number and track address
  409.  
  410.         mov     ebx,[Voice]
  411.         mov     eax,[Volume]
  412.         mov     esi,ebx
  413.         imul    esi,size track
  414.  
  415. ; set the voice and track volume
  416.  
  417.         mov     [esi+tracks.volume],al
  418.         mul     [samplevolume]
  419.         mov     [byte 4*ebx+voicevolume],ah
  420.  
  421.         sti
  422.         popad
  423.         ret
  424. endp    MODSetVolume
  425.  
  426. ;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
  427. ; MODSetMusicVolume - set the global music volume
  428. ;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
  429. proc    MODSetMusicVolume Volume:dword
  430.         pushad
  431.         cli
  432.  
  433. ; set new music volume
  434.  
  435.         mov     eax,[Volume]
  436.         mov     [musicvolume],al
  437.  
  438. ; update all the music voices
  439.  
  440.         lea     esi,[tracks]
  441.         xor     ebx,ebx
  442. setmusicvolumel0:
  443.         mov     al,[esi+track.volume]
  444.         mul     [musicvolume]
  445.         mov     [byte 4*ebx+voicevolume],ah
  446.         add     esi,size track
  447.         inc     ebx
  448.         cmp     bx,[numtracks]
  449.         jb      setmusicvolumel0
  450.  
  451.         sti
  452.         popad
  453.         ret
  454. endp    MODSetMusicVolume
  455.  
  456. ;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
  457. ; MODSetSampleVolume - set the global sample volume
  458. ;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
  459. proc    MODSetSampleVolume Volume:dword
  460.         pushad
  461.         cli
  462.  
  463. ; set the sample volume
  464.  
  465.         mov     eax,[Volume]
  466.         mov     [samplevolume],al
  467.  
  468. ; update all the sample voices
  469.  
  470.         lea     esi,[tracks]
  471.         xor     ebx,ebx
  472. setsamplevolumel0:
  473.         cmp     bx,[numtracks]
  474.         jb      setsamplevolumef0
  475.         mov     al,[esi+track.volume]
  476.         mul     [samplevolume]
  477.         mov     [byte 4*ebx+voicevolume],ah
  478. setsamplevolumef0:
  479.         add     esi,size track
  480.         inc     ebx
  481.         cmp     bx,MAXVOICES
  482.         jb      setsamplevolumel0
  483.  
  484.         sti
  485.         popad
  486.         ret
  487. endp    MODSetSampleVolume
  488.  
  489. ;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
  490. ; MODDetectCard - detect the Sound Blaster configuration
  491. ; Out:
  492. ;  Port = I/O Port
  493. ;  IRQ = IRQ level
  494. ;  DRQ = DMA channel
  495. ;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
  496. proc    MODDetectCard Port:dword,IRQ:dword,DRQ:dword
  497.         pushad
  498.  
  499. ; call the lowlevel autodetection routine
  500.  
  501.         call    mixdetect
  502.  
  503. ; set the parameters in the user variables
  504.  
  505.         mov     eax,[Port]
  506.         mov     [eax],dx
  507.         mov     eax,[IRQ]
  508.         mov     [eax],cl
  509.         mov     eax,[DRQ]
  510.         mov     [eax],ch
  511.  
  512.         popad
  513.         sbb     eax,eax
  514.         ret
  515. endp    MODDetectCard
  516.  
  517. ;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
  518. ; pollmodule - polls the module player
  519. ;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
  520. pollmodule:
  521.         pushad
  522.         dec     [tempocount]            ; decrease the tempo counter
  523.         jle     pollmodulef0
  524.         lea     esi,[tracks]            ; while in the same pattern row
  525.         xor     ebx,ebx                 ; update the track effects.
  526. pollmodulel0:
  527.         call    updatechannel
  528.         add     esi,size track
  529.         inc     ebx
  530.         cmp     bx,[numtracks]
  531.         jb      pollmodulel0
  532.         popad
  533.         ret
  534.  
  535. pollmodulef0:                           ; advance to the next pattern row.
  536.         mov     al,[tempo]              ; update the tempo counter
  537.         mov     [tempocount],al
  538.         mov     edx,[moduleptr]         ; get the module and pattern address
  539.         mov     edi,[pattptr]
  540.         cmp     [pattrow],40h           ; need to advance to the next order?
  541.         jb      pollmodulef2
  542.         xor     eax,eax                 ; reset the pattern row
  543.         mov     [pattrow],al
  544.         mov     al,[orderpos]           ; if we are at the end of the order
  545.         cmp     al,[orderlen]           ; list, loop to the beginning
  546.         jb      pollmodulef1
  547.         xor     al,al
  548.         mov     [orderpos],al
  549. pollmodulef1:
  550.         inc     [orderpos]              ; get the new pattern address
  551.         movzx   eax,[edx+eax+module.orders]
  552.         mov     edi,[edx+4*eax+module.patterns]
  553. pollmodulef2:
  554.         inc     [pattrow]               ; increase pattern row number
  555.         lea     esi,[tracks]
  556.         xor     ebx,ebx                 ; read and interpret the next
  557. pollmodulel1:                           ; pattern row of events
  558.         call    readchannel
  559.         add     esi,size track
  560.         add     edi,4
  561.         inc     ebx
  562.         cmp     bx,[numtracks]
  563.         jb      pollmodulel1
  564.         mov     [pattptr],edi           ; save pattern row address
  565.         popad
  566.         ret
  567.  
  568. ;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
  569. ; readchannel - read the next note event from the pattern sheet
  570. ; In:
  571. ;  EBX = voice number
  572. ;  ESI = track address
  573. ;  EDI = pattern address
  574. ;  EDX = module address
  575. ;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
  576. readchannel:
  577.         pushad
  578.  
  579. ; check for new sample number . . .
  580.  
  581.         mov     al,[edi+1]
  582.         test    al,al
  583.         je      readchannelf0
  584.         mov     [esi+track.inst],al
  585.         movzx   eax,al
  586.         mov     al,[edx+eax+module.sampvolume]
  587.         mov     [esi+track.volume],al
  588.         mul     [musicvolume]
  589.         mov     [byte 4*ebx+voicevolume],ah
  590.  
  591. ; check for new note pitch . . .
  592.  
  593. readchannelf0:
  594.         mov     al,[edi]
  595.         test    al,al
  596.         je      readchannelf1
  597.         movzx   eax,al
  598.         mov     [esi+track.note],ax
  599.         cmp     [byte edi+3],03h
  600.         je      readchannelf1
  601.         mov     ax,[2*eax+periodtable]
  602.         mov     [esi+track.period],ax
  603.         mov     eax,[4*eax+pitchtable]
  604.         mov     [4*ebx+voicepitch],eax
  605.         movzx   eax,[esi+track.inst]
  606.         lea     edx,[4*eax+edx]
  607.         mov     eax,[edx+module.sampptr]
  608.         mov     [4*ebx+voicepos],eax
  609.         mov     eax,[edx+module.sampend]
  610.         mov     [4*ebx+voiceend],eax
  611.         mov     eax,[edx+module.samploop]
  612.         mov     [4*ebx+voiceloop],eax
  613.  
  614. ; check the new track effect . . .
  615.  
  616. readchannelf1:
  617.         mov     dx,[edi+2]
  618.         mov     [esi+track.effect],dx
  619.         movzx   eax,dh
  620.         and     al,0Fh
  621.         call    [4*eax+efxtable]
  622.  
  623.         popad
  624.         ret
  625.  
  626. ;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
  627. ; updatechannel - update the track using the current effect
  628. ; In:
  629. ;  EBX = voice number
  630. ;  ESI = track address
  631. ;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
  632. updatechannel:
  633.         pushad
  634.         mov     dx,[esi+track.effect]
  635.         movzx   eax,dh
  636.         and     al,0Fh
  637.         call    [4*eax+efxtable2]
  638.         popad
  639.         ret
  640.  
  641. ;░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒
  642. ; Protracker effects stuff
  643. ;░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒
  644.  
  645. ;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
  646. ; Effect jump tables
  647. ;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
  648.  
  649.         align   4
  650.  
  651. label efxtable dword
  652.         dd      efxarpeggio             ; 0 - arpeggio
  653.         dd      efxnull                 ; 1 - porta up
  654.         dd      efxnull                 ; 2 - porta down
  655.         dd      efxtoneporta            ; 3 - tone porta
  656.         dd      efxvibrato              ; 4 - vibrato
  657.         dd      efxnull                 ; 5 - tone+slide
  658.         dd      efxnull                 ; 6 - vibrato+slide
  659.         dd      efxtremolo              ; 7 - tremolo
  660.         dd      efxnull                 ; 8 - unused
  661.         dd      efxsampoffset           ; 9 - sample offset
  662.         dd      efxnull                 ; A - volume slide
  663.         dd      efxpattjump             ; B - pattern jump
  664.         dd      efxsetvolume            ; C - set volume
  665.         dd      efxbreak                ; D - break pattern
  666.         dd      efxnull                 ; E - extra effects
  667.         dd      efxsetspeed             ; F - set speed
  668.  
  669. label efxtable2 dword
  670.         dd      efxarpeggio2            ; 0 - arpeggio
  671.         dd      efxportaup              ; 1 - porta up
  672.         dd      efxportadown            ; 2 - porta down
  673.         dd      efxtoneporta2           ; 3 - tone porta
  674.         dd      efxvibrato2             ; 4 - vibrato
  675.         dd      efxtoneslide            ; 5 - tone+slide
  676.         dd      efxvibslide             ; 6 - vibrato+slide
  677.         dd      efxtremolo2             ; 7 - tremolo
  678.         dd      efxnull                 ; 8 - unused
  679.         dd      efxnull                 ; 9 - sample offset
  680.         dd      efxvolslide             ; A - volume slide
  681.         dd      efxnull                 ; B - pattern jump
  682.         dd      efxnull                 ; C - set volume
  683.         dd      efxnull                 ; D - break pattern
  684.         dd      efxnull                 ; E - extra effects
  685.         dd      efxnull                 ; F - set speed
  686.  
  687. ;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
  688. ; efxnull - dummy effect
  689. ; In:
  690. ;  EBX = voice number
  691. ;  ESI = track address
  692. ;  DL = effect parameter
  693. ;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
  694. efxnull:
  695.         ret
  696.  
  697. ;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
  698. ; efxarpeggio - arpeggio
  699. ; In:
  700. ;  EBX = voice number
  701. ;  ESI = track address
  702. ;  DL = effect parameter
  703. ;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
  704. efxarpeggio:
  705.         test    dl,dl
  706.         je      efxnull
  707.         mov     dh,dl
  708.         and     dl,0Fh
  709.         shr     dh,4
  710.         movzx   eax,[esi+track.note]
  711.         mov     cx,[2*eax+periodtable]
  712.         mov     [esi+track.arptable],cx
  713.         add     al,dh
  714.         mov     cx,[2*eax+periodtable]
  715.         mov     [esi+2+track.arptable],cx
  716.         sub     al,dh
  717.         add     al,dl
  718.         mov     cx,[2*eax+periodtable]
  719.         mov     [esi+4+track.arptable],cx
  720.         ret
  721. efxarpeggio2:
  722.         test    dl,dl
  723.         je      efxnull
  724.         movzx   eax,[esi+track.arptable]
  725.         xchg    [esi+4+track.arptable],ax
  726.         xchg    [esi+2+track.arptable],ax
  727.         mov     [esi+track.arptable],ax
  728.         mov     eax,[4*eax+pitchtable]
  729.         mov     [4*ebx+voicepitch],eax
  730.         ret
  731.  
  732. ;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
  733. ; efxportaup - slides the pitch up
  734. ; In:
  735. ;  EBX = voice number
  736. ;  ESI = track address
  737. ;  DL = effect parameter
  738. ;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
  739. efxportaup:
  740.         xor     dh,dh
  741.         movzx   eax,[esi+track.period]
  742.         sub     ax,dx
  743.         cmp     ax,28
  744.         jge     efxportaupf0
  745.         mov     ax,28
  746. efxportaupf0:
  747.         mov     [esi+track.period],ax
  748.         mov     eax,[4*eax+pitchtable]
  749.         mov     [4*ebx+voicepitch],eax
  750.         ret
  751.  
  752. ;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
  753. ; efxportadown - slides the pitch down
  754. ; In:
  755. ;  EBX = voice number
  756. ;  ESI = track address
  757. ;  DL = effect parameter
  758. ;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
  759. efxportadown:
  760.         xor     dh,dh
  761.         movzx   eax,[esi+track.period]
  762.         add     ax,dx
  763.         cmp     ax,3424
  764.         jle     efxportadownf0
  765.         mov     ax,3424
  766. efxportadownf0:
  767.         mov     [esi+track.period],ax
  768.         mov     eax,[4*eax+pitchtable]
  769.         mov     [4*ebx+voicepitch],eax
  770.         ret
  771.  
  772. ;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
  773. ; efxtoneporta - tone portamento
  774. ; In:
  775. ;  EBX = voice number
  776. ;  ESI = track address
  777. ;  DL = effect parameter
  778. ;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
  779. efxtoneporta:
  780.         test    dl,dl
  781.         jne     efxtoneportaf0
  782.         mov     dl,[esi+track.tonespeed]
  783. efxtoneportaf0:
  784.         mov     [esi+track.tonespeed],dl
  785.         mov     [esi+track.effect],dx
  786.         movzx   eax,[esi+track.note]
  787.         mov     ax,[2*eax+periodtable]
  788.         mov     [esi+track.destperiod],ax
  789.         ret
  790. efxtoneporta2:
  791.         xor     dh,dh
  792.         movzx   eax,[esi+track.period]
  793.         mov     cx,[esi+track.destperiod]
  794.         cmp     ax,cx
  795.         je      efxnull
  796.         jg      efxtoneportaf1
  797.         add     ax,dx
  798.         cmp     ax,cx
  799.         jle     efxtoneportaf2
  800.         mov     ax,cx
  801. efxtoneportaf2:
  802.         mov     [esi+track.period],ax
  803.         mov     eax,[4*eax+pitchtable]
  804.         mov     [4*ebx+voicepitch],eax
  805.         ret
  806. efxtoneportaf1:
  807.         sub     ax,dx
  808.         cmp     ax,cx
  809.         jge     efxtoneportaf3
  810.         mov     ax,cx
  811. efxtoneportaf3:
  812.         mov     [esi+track.period],ax
  813.         mov     eax,[4*eax+pitchtable]
  814.         mov     [4*ebx+voicepitch],eax
  815.         ret
  816.  
  817. ;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
  818. ; efxvibrato - pitch vibrato
  819. ; In:
  820. ;  EBX = voice number
  821. ;  ESI = track address
  822. ;  DL = effect parameter
  823. ;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
  824. efxvibrato:
  825.         mov     al,[esi+track.vibparm]
  826.         mov     ah,al
  827.         and     ax,0F00Fh
  828.         test    dl,0Fh
  829.         jne     efxvibratof0
  830.         or      dl,al
  831. efxvibratof0:
  832.         test    dl,0F0h
  833.         jne     efxvibratof1
  834.         or      dl,ah
  835. efxvibratof1:
  836.         mov     [esi+track.vibparm],dl
  837.         mov     [esi+track.effect],dx
  838.         ret
  839. efxvibrato2:
  840.         mov     dh,dl
  841.         and     dx,0F00Fh
  842.         shr     dh,2
  843.         mov     al,[esi+track.vibpos]
  844.         add     [esi+track.vibpos],dh
  845.         mov     dh,al
  846.         shr     al,2
  847.         and     eax,1Fh
  848.         mov     al,[eax+sintable]
  849.         mul     dl
  850.         shr     ax,7
  851.         test    dh,dh
  852.         jge     efxvibratof2
  853.         neg     ax
  854. efxvibratof2:
  855.         add     ax,[esi+track.period]
  856.         cmp     ax,28
  857.         jge     efxvibratof3
  858.         mov     ax,28
  859. efxvibratof3:
  860.         cmp     ax,3424
  861.         jle     efxvibratof4
  862.         mov     ax,3424
  863. efxvibratof4:
  864.         movzx   eax,ax
  865.         mov     eax,[4*eax+pitchtable]
  866.         mov     [4*ebx+voicepitch],eax
  867.         ret
  868.  
  869. ;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
  870. ; efxtoneslide - volume slide and continue last portamento
  871. ; In:
  872. ;  EBX = voice number
  873. ;  ESI = track address
  874. ;  DL = effect parameter
  875. ;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
  876. efxtoneslide:
  877.         call    efxvolslide
  878.         mov     dl,[esi+track.tonespeed]
  879.         jmp     efxtoneporta
  880.  
  881. ;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
  882. ; efxvibslide - volume slide and continue last pitch vibrato
  883. ; In:
  884. ;  EBX = voice number
  885. ;  ESI = track address
  886. ;  DL = effect parameter
  887. ;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
  888. efxvibslide:
  889.         call    efxvolslide
  890.         mov     dl,[esi+track.vibparm]
  891.         jmp     efxvibrato2
  892.  
  893. ;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
  894. ; efxtremolo - volume vibrato
  895. ; In:
  896. ;  EBX = voice number
  897. ;  ESI = track address
  898. ;  DL = effect parameter
  899. ;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
  900. efxtremolo:
  901.         mov     al,[esi+track.tremparm]
  902.         mov     ah,al
  903.         and     ax,0F00Fh
  904.         test    dl,0Fh
  905.         jne     efxtremolof0
  906.         or      dl,al
  907. efxtremolof0:
  908.         test    dl,0F0h
  909.         jne     efxtremolof1
  910.         or      dl,ah
  911. efxtremolof1:
  912.         mov     [esi+track.tremparm],dl
  913.         mov     [esi+track.effect],dx
  914.         ret
  915. efxtremolo2:
  916.         mov     dh,dl
  917.         and     dx,0F00Fh
  918.         shr     dh,2
  919.         mov     al,[esi+track.trempos]
  920.         add     [esi+track.trempos],dh
  921.         mov     dh,al
  922.         shr     al,2
  923.         and     eax,1Fh
  924.         mov     al,[eax+sintable]
  925.         mul     dl
  926.         shr     ax,6
  927.         test    dh,dh
  928.         jge     efxtremolof2
  929.         neg     ax
  930. efxtremolof2:
  931.         add     al,[esi+track.volume]
  932.         jge     efxtremolof3
  933.         xor     al,al
  934. efxtremolof3:
  935.         cmp     al,40h
  936.         jle     efxtremolof4
  937.         mov     al,40h
  938. efxtremolof4:
  939.         mul     [musicvolume]
  940.         mov     [byte 4*ebx+voicevolume],ah
  941.         ret
  942.  
  943. ;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
  944. ; efxsampoffset - set the sample offset
  945. ; In:
  946. ;  EBX = voice number
  947. ;  ESI = track address
  948. ;  DL = effect parameter
  949. ;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
  950. efxsampoffset:
  951.         movzx   eax,[esi+track.inst]
  952.         mov     esi,[moduleptr]
  953.         mov     eax,[esi+4*eax+module.sampptr]
  954.         movzx   edx,dl
  955.         shl     edx,8
  956.         add     eax,edx
  957.         mov     [4*ebx+voicepos],eax
  958.         ret
  959.  
  960. ;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
  961. ; efxvolslide - volume slide
  962. ; In:
  963. ;  EBX = voice number
  964. ;  ESI = track address
  965. ;  DL = effect parameter
  966. ;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
  967. efxvolslide:
  968.         mov     al,[esi+track.volume]
  969.         mov     dh,dl
  970.         shr     dl,4
  971.         je      efxvolslidef0
  972.         add     al,dl
  973.         cmp     al,40h
  974.         jle     efxvolslidef1
  975.         mov     al,40h
  976. efxvolslidef1:
  977.         mov     [esi+track.volume],al
  978.         mul     [musicvolume]
  979.         mov     [byte 4*ebx+voicevolume],ah
  980.         ret
  981. efxvolslidef0:
  982.         sub     al,dh
  983.         jge     efxvolslidef2
  984.         xor     al,al
  985. efxvolslidef2:
  986.         mov     [esi+track.volume],al
  987.         mul     [musicvolume]
  988.         mov     [byte 4*ebx+voicevolume],ah
  989.         ret
  990.  
  991. ;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
  992. ; efxpattjump - jump to order pattern
  993. ; In:
  994. ;  EBX = voice number
  995. ;  ESI = track address
  996. ;  DL = effect parameter
  997. ;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
  998. efxpattjump:
  999.         mov     [orderpos],dl
  1000.         mov     [pattrow],40h
  1001.         ret
  1002.  
  1003. ;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
  1004. ; efxsetvolume - set volume
  1005. ; In:
  1006. ;  EBX = voice number
  1007. ;  ESI = track address
  1008. ;  DL = effect parameter
  1009. ;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
  1010. efxsetvolume:
  1011.         mov     al,dl
  1012.         mov     [esi+track.volume],al
  1013.         mul     [musicvolume]
  1014.         mov     [byte 4*ebx+voicevolume],ah
  1015.         ret
  1016.  
  1017. ;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
  1018. ; efxbreak - break pattern
  1019. ; In:
  1020. ;  EBX = voice number
  1021. ;  ESI = track address
  1022. ;  DL = effect parameter
  1023. ;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
  1024. efxbreak:
  1025.         mov     [pattrow],40h
  1026.         ret
  1027.  
  1028. ;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
  1029. ; efxsetspeed - set the tempo or BPM speed
  1030. ; In:
  1031. ;  EBX = voice number
  1032. ;  ESI = track address
  1033. ;  DL = effect parameter
  1034. ;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
  1035. efxsetspeed:
  1036.         test    dl,dl
  1037.         je      efxnull
  1038.         cmp     dl,20h
  1039.         jae     efxsetbpm
  1040.         mov     [tempo],dl
  1041.         mov     [tempocount],dl
  1042.         ret
  1043. efxsetbpm:
  1044.         mov     [bpm],dl
  1045.         call    mixstarttimer
  1046.         ret
  1047.  
  1048. ;░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒
  1049. ; Sound Blaster Driver highlevel stuff
  1050. ;░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒
  1051.  
  1052. ;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
  1053. ; mixinit - initialize the sound driver
  1054. ; In:
  1055. ;  AX = mixing speed in hertz
  1056. ;  BL = number of voices
  1057. ;  DX = I/O port address
  1058. ;  CL = IRQ level
  1059. ;  CH = DRQ channel
  1060. ;  BH = polling mode
  1061. ; Out:
  1062. ;  CF = status
  1063. ;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
  1064. mixinit:
  1065.         pushad
  1066.  
  1067.         cmp     [playing],0
  1068.         stc
  1069.         jne     mixinitd0
  1070.  
  1071. ; setup sound card parameters
  1072.  
  1073.         mov     [manualmode],bh
  1074.         xor     bh,bh
  1075.         mov     [mixfreq],ax
  1076.         mov     [numvoices],bx
  1077.         mov     [ioaddr],dx
  1078.         mov     [irqnum],cl
  1079.         mov     [drqnum],ch
  1080.  
  1081. ; check if the sound card is present
  1082.  
  1083.         call    sbreset
  1084.         jc      mixinitd0
  1085.  
  1086. ; setup timer and double buffer variables
  1087.  
  1088.         mov     [timerproc],offset nulltimer
  1089.         xor     eax,eax
  1090.         mov     [bufoff],eax
  1091.         mov     [timeracc],eax
  1092.         mov     [timerspeed],256
  1093.  
  1094. ; clear voice parameters
  1095.  
  1096.         push    es
  1097.         mov     ax,ds
  1098.         mov     es,ax
  1099.         cld
  1100.         lea     edi,[voicepos]
  1101.         mov     ecx,6*MAXVOICES
  1102.         xor     eax,eax
  1103.         rep     stosd
  1104.         pop     es
  1105.  
  1106. ; allocate conventional memory for the DMA buffer, the volume table,
  1107. ; the mixing buffer and the boosting table.
  1108.  
  1109.         mov     ax,0100h
  1110.         mov     bx,(DMABUFLEN+VOLBUFLEN+MIXBUFLEN+15)/16
  1111.         int     31h
  1112.         jc      mixinitd0
  1113.         mov     [bufsel],dx
  1114.         movzx   eax,ax
  1115.         shl     eax,4
  1116.  
  1117. ; set the address of the mixing buffer and boosting table
  1118.  
  1119.         mov     [mixbuffer],eax
  1120.         add     eax,2*DMABUFLEN
  1121.         mov     [boosttable],eax
  1122.         add     eax,2048
  1123.  
  1124. ; get the address of the DMA buffer and volume table
  1125.  
  1126.         mov     ecx,DMABUFLEN
  1127.         lea     edx,[eax+ecx]
  1128.  
  1129. ; check for cross-pages in the DMA buffer and align the Volume table
  1130.  
  1131.         mov     esi,eax
  1132.         add     si,cx
  1133.         jnc     mixinitf0
  1134.         mov     edx,eax
  1135.         add     eax,VOLBUFLEN
  1136. mixinitf0:
  1137.         add     edx,255
  1138.         xor     dl,dl
  1139.         mov     [bufptr],eax
  1140.         mov     [voltable],edx
  1141.  
  1142. ; clear DMA buffer with centered samples
  1143.  
  1144.         push    es
  1145.         mov     ax,ds
  1146.         mov     es,ax
  1147.         cld
  1148.         mov     edi,[bufptr]
  1149.         mov     ecx,DMABUFLEN
  1150.         mov     al,80h
  1151.         rep     stosb
  1152.         pop     es
  1153.  
  1154. ; build volume table and boosting table
  1155.  
  1156.         mov     cl,6
  1157.         mov     edi,[voltable]
  1158.         xor     bx,bx
  1159. mixinitl0:
  1160.         mov     al,bl
  1161.         imul    bh
  1162.         sar     ax,cl
  1163.         mov     [edi],al
  1164.         inc     edi
  1165.         inc     bl
  1166.         jne     mixinitl0
  1167.         inc     bh
  1168.         cmp     bh,40h
  1169.         jbe     mixinitl0
  1170.  
  1171.         push    es
  1172.         mov     ax,ds
  1173.         mov     es,ax
  1174.         cld
  1175.         mov     edi,[boosttable]
  1176.         mov     ecx,768
  1177.         xor     ax,ax
  1178.         rep     stosb
  1179.         mov     ecx,512
  1180. mixinitl1:
  1181.         mov     [edi],ah
  1182.         add     ax,80h
  1183.         inc     edi
  1184.         loop    mixinitl1
  1185.         mov     ecx,768
  1186.         dec     al
  1187.         rep     stosb
  1188.         pop     es
  1189.  
  1190. ; initialize the sound card for output
  1191.  
  1192.         call    dmasetup
  1193.         call    irqsetup
  1194.         call    sbsetup
  1195.  
  1196. ; dont use the timer interrupt for manual polling mode
  1197.  
  1198.         cmp     [manualmode],0
  1199.         jne     mixinitf1
  1200.  
  1201. ; install timer interrupt to poll the driver
  1202.  
  1203.         push    es
  1204.         mov     ax,cs
  1205.         mov     es,ax
  1206.         lea     ebx,[mixtimer]
  1207.         mov     cl,0
  1208.         call    irqsetvect
  1209.         mov     [oldtimeroff],ebx
  1210.         mov     [oldtimersel],es
  1211.         pop     es
  1212.  
  1213. ; set the timer frequency to 70 hertz
  1214.  
  1215.         cli
  1216.         mov     al,36h
  1217.         out     43h,al
  1218.         mov     ax,TIMERRATE
  1219.         out     40h,al
  1220.         mov     al,ah
  1221.         out     40h,al
  1222.         sti
  1223.  
  1224. ; set driver playing status
  1225.  
  1226. mixinitf1:
  1227.         mov     [playing],1
  1228.         clc
  1229.  
  1230. mixinitd0:
  1231.         popad
  1232.         ret
  1233.  
  1234. ;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
  1235. ; mixdone - deinitialize the sound driver
  1236. ;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
  1237. mixdone:
  1238.         pushad
  1239.  
  1240.         cmp     [playing],1
  1241.         jne     mixdoned0
  1242.  
  1243. ; the timer interrupt was modified if we are using the timer polling mode
  1244.  
  1245.         cmp     [manualmode],0
  1246.         jne     mixdonef0
  1247.  
  1248. ; restore the timer frequency to 18.2 hertz
  1249.  
  1250.         cli
  1251.         mov     al,36h
  1252.         out     43h,al
  1253.         xor     al,al
  1254.         out     40h,al
  1255.         out     40h,al
  1256.         sti
  1257.  
  1258. ; deinstall timer interrupt used to poll the driver
  1259.  
  1260.         push    es
  1261.         mov     ebx,[oldtimeroff]
  1262.         mov     es,[oldtimersel]
  1263.         mov     cl,0
  1264.         call    irqsetvect
  1265.         pop     es
  1266.  
  1267. ; deinitialize the sound card output
  1268.  
  1269. mixdonef0:
  1270.         call    sbdone
  1271.         call    irqdone
  1272.         call    dmadone
  1273.  
  1274. ; free conventional memory block used for DMA buffer, volume table,
  1275. ; mixing buffer and boosting table.
  1276.  
  1277.         mov     ax,0101h
  1278.         mov     dx,[bufsel]
  1279.         int     31h
  1280.  
  1281. ; set driver stopped status
  1282.  
  1283.         mov     [playing],0
  1284.  
  1285. mixdoned0:
  1286.         popad
  1287.         ret
  1288.  
  1289. ;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
  1290. ; mixtimer - timer interrupt routine used to poll the sound driver
  1291. ;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
  1292. mixtimer:
  1293.         push    eax
  1294.         push    ds
  1295.         mov     ds,[cs:datasel]
  1296.         call    mixpoll                 ; poll the sound system
  1297.         add     [oldtimeracc],TIMERRATE
  1298.         jnc     mixtimerf0              ; time to call the old IRQ0 vector?
  1299.         pop     ds                      ; yes, jump to the old IRQ0 service
  1300.         pop     eax
  1301.         jmp     [fword cs:oldtimeroff]
  1302. mixtimerf0:
  1303.         mov     al,20h                  ; nope, send PIC acknowledge and exit
  1304.         out     20h,al
  1305.         pop     ds
  1306.         pop     eax
  1307.         iretd
  1308.  
  1309. ;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
  1310. ; mixdetect - detect the sound card configuration
  1311. ; Out:
  1312. ;  DX = I/O Port
  1313. ;  CL = IRQ level
  1314. ;  CH = DMA channel
  1315. ;  CF = status
  1316. ;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
  1317. mixdetect:
  1318.         cmp     [playing],1             ; do not try to autodetect
  1319.         stc                             ; if we are already playing
  1320.         je      mixdetectd0
  1321.         call    sbdetect
  1322. mixdetectd0:
  1323.         ret
  1324.  
  1325. ;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
  1326. ; mixsettimerproc - set the timer procedure
  1327. ; In:
  1328. ;  EDX = timer routine address
  1329. ;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
  1330. mixsettimerproc:
  1331.         mov     [timerproc],edx         ; set the timer callback address
  1332.         ret
  1333.  
  1334. ;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
  1335. ; mixstarttimer - start the timer at the specified speed
  1336. ; In:
  1337. ;  DL = timer speed in beats per minute (BPMs)
  1338. ;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
  1339. mixstarttimer:
  1340.         push    eax
  1341.         push    ebx
  1342.         push    edx
  1343.         mov     bh,dl                   ; set the timer callback speed
  1344.         xor     bl,bl                   ; to 24/60*BPM hertz
  1345.         mov     ax,[mixfreq]
  1346.         mov     dx,0280h
  1347.         mul     dx
  1348.         div     bx
  1349.         movzx   eax,ax
  1350.         mov     [timerspeed],eax
  1351.         pop     edx
  1352.         pop     ebx
  1353.         pop     eax
  1354.         ret
  1355.  
  1356. ;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
  1357. ; mixstoptimer - stop the timer routine
  1358. ;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
  1359. mixstoptimer:
  1360.         mov     [timerproc],offset nulltimer
  1361. nulltimer:
  1362.         ret
  1363.  
  1364. ;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
  1365. ; mixvoice - mixes the voice samples
  1366. ; In:
  1367. ;  EBX = voice number (*4)
  1368. ;  ECX = number of samples
  1369. ;  EDI = buffer address
  1370. ; Out:
  1371. ;  EDI = buffer end address
  1372. ;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
  1373. mixvoice:
  1374.  
  1375.         macro   mixcode OPCODE
  1376.         local   mixcodel0,mixjmptable
  1377.  
  1378.         push    eax
  1379.         push    ebx
  1380.         push    ecx
  1381.         push    edx
  1382.         push    ebp
  1383.         push    esi
  1384.  
  1385.         push    ebx
  1386.         mov     esi,[ebx+voicepos]
  1387.         mov     dl,[byte ebx+3+voicefrac]
  1388.         mov     dh,[byte ebx+1+voicepitch]
  1389.         movzx   ebp,[word ebx+2+voicepitch]
  1390.         mov     ebx,[ebx+voicevolume]
  1391.         mov     bh,bl
  1392.         add     ebx,[voltable]
  1393.  
  1394.         movzx   eax,cl
  1395.         and     al,31
  1396.         shr     ecx,5
  1397.         lea     edi,[edi+4*eax-4*32]
  1398.         jmp     [4*eax+mixjmptable]
  1399.  
  1400.         align   4
  1401. mixcodel0:
  1402.         I=0
  1403.         rept    32
  1404. CODESTART=$
  1405.         mov     bl,[esi]
  1406.         add     dl,dh
  1407.         movsx   eax,[byte ebx]
  1408.         adc     esi,ebp
  1409.         OPCODE  [edi+I],eax
  1410. CODELEN=$-CODESTART
  1411.         I=I+4
  1412.         endm
  1413.         add     edi,4*32
  1414.         dec     ecx
  1415.         jge     mixcodel0
  1416.  
  1417.         pop     ebx
  1418.         mov     [ebx+voicepos],esi
  1419.         mov     [byte ebx+3+voicefrac],dl
  1420.  
  1421.         pop     esi
  1422.         pop     ebp
  1423.         pop     edx
  1424.         pop     ecx
  1425.         pop     ebx
  1426.         pop     eax
  1427.         ret
  1428.  
  1429.         align   4
  1430.         label mixjmptable dword
  1431.         I=CODESTART+CODELEN
  1432.         rept    32
  1433.         dd      I
  1434.         I=I-CODELEN
  1435.         endm
  1436.         endm
  1437.  
  1438.         test    ebx,ebx
  1439.         je      mixvoicef0
  1440.         mixcode add
  1441. mixvoicef0:
  1442.         mixcode mov
  1443.  
  1444. ;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
  1445. ; mixvoices - mixes all the voices
  1446. ; In:
  1447. ;  EDI = buffer address
  1448. ;  ECX = number of samples
  1449. ;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
  1450. mixvoices:
  1451.         pushad
  1452.  
  1453.         xor     ebx,ebx
  1454. mixvoicesl0:
  1455.         push    ebx
  1456.         push    ecx
  1457.         push    edi
  1458.         shl     ebx,2
  1459.         mov     ebp,ecx
  1460.  
  1461. mixvoicesl1:
  1462.         mov     ecx,ebp
  1463.         mov     edx,[ebx+voicepos]
  1464.         mov     eax,[ebx+voiceend]
  1465.         cmp     edx,eax
  1466.         jb      mixvoicesf0
  1467.         sub     edx,eax
  1468.         add     edx,[ebx+voiceloop]
  1469.         cmp     edx,eax
  1470.         jae     mixvoicesc0
  1471.         mov     [ebx+voicepos],edx
  1472.  
  1473. mixvoicesf0:
  1474.         sub     eax,edx
  1475.         shl     eax,16
  1476.         mov     edx,[ebx+voicefrac]
  1477.         shr     edx,16
  1478.         sub     eax,edx
  1479.  
  1480.         mov     edx,ecx
  1481.         mov     esi,[ebx+voicepitch]
  1482.         imul    edx,esi
  1483.         cmp     edx,eax
  1484.         jbe     mixvoicesf1
  1485.         dec     eax
  1486.         xor     edx,edx
  1487.         add     eax,esi
  1488.         adc     edx,edx
  1489.         div     esi
  1490.         mov     ecx,eax
  1491.  
  1492. mixvoicesf1:
  1493.         call    mixvoice
  1494.         sub     ebp,ecx
  1495.         jg      mixvoicesl1
  1496.  
  1497.         pop     edi
  1498.         pop     ecx
  1499.         pop     ebx
  1500.         inc     ebx
  1501.         cmp     bx,[numvoices]
  1502.         jb      mixvoicesl0
  1503.  
  1504.         popad
  1505.         ret
  1506.  
  1507. mixvoicesc0:
  1508.         test    ebx,ebx
  1509.         jne     mixvoicesc1
  1510.         push    es
  1511.         mov     ax,ds
  1512.         mov     es,ax
  1513.         xor     eax,eax
  1514.         cld
  1515.         rep     stosd
  1516.         pop     es
  1517.  
  1518. mixvoicesc1:
  1519.         pop     edi
  1520.         pop     ecx
  1521.         pop     ebx
  1522.         inc     ebx
  1523.         cmp     bx,[numvoices]
  1524.         jb      mixvoicesl0
  1525.  
  1526.         popad
  1527.         ret
  1528.  
  1529. ;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
  1530. ; mixpoll - updates the output buffer
  1531. ;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
  1532. mixpoll:
  1533.         pushad
  1534.  
  1535. ; get the 32 bit mixing buffer address
  1536.  
  1537.         mov     ecx,DMABUFLEN/2
  1538.         mov     edi,[mixbuffer]
  1539.  
  1540. ; check if we can fill the current half buffer with samples
  1541.  
  1542.         call    dmagetpos
  1543.         cmp     eax,ecx
  1544.         jae     mixpollf3
  1545.         cmp     [bufoff],ecx
  1546.         je      mixpollf4
  1547.         jmp     mixpolld0
  1548. mixpollf3:
  1549.         cmp     [bufoff],ecx
  1550.         je      mixpolld0
  1551.  
  1552. ; fill the mixing buffer and polls the timer callback routine
  1553.  
  1554. mixpollf4:
  1555.         mov     eax,[timeracc]
  1556.         mov     ebp,ecx
  1557. mixpolll0:
  1558.         test    eax,eax
  1559.         jg      mixpollf0
  1560.         call    [timerproc]
  1561.         add     eax,[timerspeed]
  1562. mixpollf0:
  1563.         mov     ecx,eax
  1564.         add     ecx,63
  1565.         and     cl,not 63
  1566.         cmp     ecx,ebp
  1567.         jle     mixpollf1
  1568.         mov     ecx,ebp
  1569. mixpollf1:
  1570.         call    mixvoices
  1571.         lea     edi,[edi+4*ecx]
  1572.         sub     eax,ecx
  1573.         sub     ebp,ecx
  1574.         jg      mixpolll0
  1575.         mov     [timeracc],eax
  1576.  
  1577. ; translate 32-bit signed samples to 8-bit unsigned samples
  1578.  
  1579.         mov     ecx,DMABUFLEN/2
  1580.         mov     esi,[mixbuffer]
  1581.         mov     edi,[bufptr]
  1582.         add     edi,[bufoff]
  1583.         xor     [bufoff],ecx
  1584.         mov     ebx,[boosttable]
  1585.         add     ebx,1024
  1586.         shr     ecx,4
  1587. mixpolll2:
  1588.         I=0
  1589.         rept    4
  1590.         mov     eax,[esi+16*I+8]
  1591.         mov     dl,[eax+ebx]
  1592.         mov     eax,[esi+16*I+12]
  1593.         mov     dh,[eax+ebx]
  1594.         shl     edx,16
  1595.         mov     eax,[esi+16*I]
  1596.         mov     dl,[eax+ebx]
  1597.         mov     eax,[esi+16*I+4]
  1598.         mov     dh,[eax+ebx]
  1599.         mov     [edi+4*I],edx
  1600.         I=I+1
  1601.         endm
  1602.         add     esi,4*16
  1603.         add     edi,16
  1604.         dec     ecx
  1605.         jg      mixpolll2
  1606.  
  1607. mixpolld0:
  1608.         popad
  1609.         ret
  1610.  
  1611. ;░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒
  1612. ; Sound Blaster DSP lowlevel stuff
  1613. ;░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒
  1614.  
  1615. ;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
  1616. ; sbwrite - send a command/data byte to the DSP chip
  1617. ; In:
  1618. ;  AL = command/data byte
  1619. ;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
  1620. sbwrite:
  1621.         push    eax
  1622.         push    ecx
  1623.         push    edx
  1624.         mov     dx,[ioaddr]
  1625.         add     dx,0Ch
  1626.         mov     ah,al
  1627.         mov     ecx,10000h              ; wait until the write buffer
  1628. sbwritel0:                              ; status port (2XCh) bit 7 is clear
  1629.         in      al,dx
  1630.         and     al,80h
  1631.         loopnz  sbwritel0
  1632.         mov     al,ah                   ; write value in the write
  1633.         out     dx,al                   ; data port (2XCh)
  1634.         pop     edx
  1635.         pop     ecx
  1636.         pop     eax
  1637.         ret
  1638.  
  1639. ;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
  1640. ; sbreset - reset the Sound Blaster DSP chip
  1641. ; Out:
  1642. ;  CF = status
  1643. ;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
  1644. sbreset:
  1645.         push    eax
  1646.         push    ebx
  1647.         push    ecx
  1648.         push    edx
  1649.         mov     ebx,64                  ; try to reset upto 64 times
  1650. sbresetl1:
  1651.         mov     dx,[ioaddr]
  1652.         add     dx,06h
  1653.         mov     al,1                    ; write 1 to the reset port (2X6h)
  1654.         out     dx,al
  1655.         xor     ah,ah                   ; wait at least 3 microseconds
  1656. sbresetl2:
  1657.         in      al,dx
  1658.         dec     ah
  1659.         jne     sbresetl2
  1660.         xor     al,al                   ; write 0 to the reset port (2X6h)
  1661.         out     dx,al
  1662.         add     dx,08h
  1663.         mov     ecx,0400h               ; wait until the data available
  1664. sbresetl0:                              ; status port (2XEh) bit 7 is set
  1665.         in      al,dx
  1666.         and     al,80h
  1667.         loopz   sbresetl0
  1668.         sub     dx,04h                  ; read the read data port (2XAh)
  1669.         in      al,dx
  1670.         cmp     al,0AAh
  1671.         clc
  1672.         je      sbresetd0               ; check the ready byte value
  1673.         dec     ebx
  1674.         jne     sbresetl1
  1675.         stc
  1676. sbresetd0:
  1677.         pop     edx
  1678.         pop     ecx
  1679.         pop     ebx
  1680.         pop     eax
  1681.         ret
  1682.  
  1683. ;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
  1684. ; sbsetup - start the DMA output
  1685. ;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
  1686. sbsetup:
  1687.         push    eax
  1688.         push    edx
  1689.         mov     al,0D1h                 ; turn on the speaker
  1690.         call    sbwrite
  1691.         mov     al,40h                  ; set the playback rate
  1692.         call    sbwrite
  1693.         mov     ax,1000
  1694.         mul     ax
  1695.         div     [mixfreq]
  1696.         neg     ax
  1697.         call    sbwrite
  1698.         mov     al,14h                  ; start the lowspeed 8 bit DMA
  1699.         call    sbwrite                 ; mode transfer to the DAC
  1700.         mov     al,0FFh
  1701.         call    sbwrite
  1702.         call    sbwrite
  1703.         pop     edx
  1704.         pop     eax
  1705.         ret
  1706.  
  1707. ;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
  1708. ; sbdone - shut down the DMA output
  1709. ;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
  1710. sbdone:
  1711.         push    eax
  1712.         call    sbreset                 ; reset the DSP chip
  1713.         mov     al,0D3h
  1714.         call    sbwrite                 ; turn off the speaker
  1715.         pop     eax
  1716.         ret
  1717.  
  1718. ;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
  1719. ; sbdetect - Detect the Sound Blaster I/O Port, IRQ level and DMA channel
  1720. ; Out:
  1721. ;  DX = I/O port address
  1722. ;  CL = IRQ level
  1723. ;  CH = DMA channel
  1724. ;  CF = status
  1725. ;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
  1726. sbdetect:
  1727.         mov     dx,210h                 ; scan the ports 210h..260h
  1728. sbdetectl0:
  1729.         mov     [ioaddr],dx             ; check if there is a SB card
  1730.         call    sbreset                 ; trying to reset the DSP chip
  1731.         jnc     sbdetectf0
  1732.         add     dx,10h
  1733.         cmp     dx,260h
  1734.         jbe     sbdetectl0
  1735.         xor     dx,dx
  1736.         xor     cx,cx
  1737.         stc
  1738.         ret
  1739. sbdetectf0:
  1740.         push    eax
  1741.         push    ebx
  1742.         push    ecx
  1743.         push    es
  1744.  
  1745.         mov     [datasel],ds
  1746.  
  1747.         irp     I,<2,3,5,7,10>          ; install IRQ traps
  1748.         push    cs
  1749.         pop     es
  1750.         lea     ebx,[irqtest&I]
  1751.         mov     cx,I
  1752.         call    irqsetvect
  1753.         call    irqsetmask
  1754.         push    ebx
  1755.         push    es
  1756.         endm
  1757.  
  1758.         mov     [irqnum],0
  1759.  
  1760.         mov     al,0F2h                 ; ask to the DSP to raise a IRQ
  1761.         call    sbwrite
  1762.  
  1763.         mov     ecx,10000h              ; wait until some IRQ occurs
  1764. sbdetectl1:
  1765.         cmp     [irqnum],0
  1766.         loope   sbdetectl1
  1767.  
  1768.         irp     I,<10,7,5,3,2>          ; deinstall IRQ traps
  1769.         pop     es
  1770.         pop     ebx
  1771.         mov     cx,0100h+I
  1772.         call    irqsetmask
  1773.         call    irqsetvect
  1774.         endm
  1775.  
  1776.         pop     es
  1777.         pop     ecx
  1778.         pop     ebx
  1779.         pop     eax
  1780.         mov     dx,[ioaddr]             ; return the SB parameters
  1781.         mov     cl,[irqnum]
  1782.         xor     ch,ch
  1783.         sub     ch,cl
  1784.         mov     ch,1
  1785.         cmc
  1786.         ret
  1787.  
  1788. ;░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒
  1789. ; Sound Blaster DMA lowlevel stuff
  1790. ;░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒
  1791.  
  1792. ;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
  1793. ; dmasetup - setup the DMA buffer parameters
  1794. ;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
  1795. dmasetup:
  1796.         push    eax
  1797.         push    ebx
  1798.         push    ecx
  1799.         push    edx
  1800.  
  1801.         mov     bl,[drqnum]
  1802.         mov     al,bl
  1803.         or      al,04h                  ; reset the DMA channel
  1804.         out     0Ah,al
  1805.         out     0Ch,al                  ; clear the flip flop
  1806.         mov     al,bl
  1807.         or      al,58h                  ; set the autoinit mode
  1808.         out     0Bh,al
  1809.         movzx   dx,bl
  1810.         add     dx,dx
  1811.         mov     eax,[bufptr]            ; set the buffer address
  1812.         out     dx,al
  1813.         mov     al,ah
  1814.         out     dx,al
  1815.         inc     dx
  1816.         mov     ax,DMABUFLEN            ; set the buffer length
  1817.         dec     ax
  1818.         out     dx,al
  1819.         mov     al,ah
  1820.         out     dx,al
  1821.         mov     edx,82818387h           ; set the buffer page
  1822.         mov     cl,bl
  1823.         shl     cl,3
  1824.         shr     edx,cl
  1825.         xor     dh,dh
  1826.         shr     eax,16
  1827.         out     dx,al
  1828.         mov     al,bl                   ; unlock the DMA channel
  1829.         out     0Ah,al
  1830.  
  1831.         pop     edx
  1832.         pop     ecx
  1833.         pop     ebx
  1834.         pop     eax
  1835.         ret
  1836.  
  1837. ;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
  1838. ; dmadone - shut down the DMA controller
  1839. ;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
  1840. dmadone:
  1841.         push    eax
  1842.         mov     al,[drqnum]             ; reset the DMA channel
  1843.         or      al,04h
  1844.         out     0Ah,al
  1845.         pop     eax
  1846.         ret
  1847.  
  1848. ;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
  1849. ; dmagetpos - return the DMA buffer relative position
  1850. ; Out:
  1851. ;  EAX = buffer relative position
  1852. ;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
  1853. dmagetpos:
  1854.         push    ecx
  1855.         push    edx
  1856.         out     0Ch,al                  ; clear the flip flop
  1857.         mov     dl,[drqnum]
  1858.         xor     dh,dh
  1859.         add     dl,dl
  1860.         inc     dl
  1861.         in      al,dx                   ; read the DMA counter
  1862.         mov     ah,al
  1863.         in      al,dx
  1864.         xchg    al,ah
  1865. dmagetposl0:
  1866.         mov     cx,ax                   ; read again the DMA counter
  1867.         in      al,dx
  1868.         mov     ah,al
  1869.         in      al,dx
  1870.         xchg    al,ah
  1871.         sub     cx,ax
  1872.         cmp     cx,+16                  ; both values are near?
  1873.         jg      dmagetposl0             ; nope, try again
  1874.         cmp     cx,-16
  1875.         jl      dmagetposl0
  1876.         movzx   eax,ax                  ; get the position relative
  1877.         neg     eax                     ; to the start of the buffer
  1878.         add     eax,DMABUFLEN
  1879.         pop     edx
  1880.         pop     ecx
  1881.         ret
  1882.  
  1883. ;░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒
  1884. ; Sound Blaster IRQ lowlevel stuff
  1885. ;░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒░▒
  1886.  
  1887. ;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
  1888. ; irqsetvect - set the IRQ handler routine
  1889. ; In:
  1890. ;  ES:EBX = IRQ handler routine address
  1891. ;  CL = IRQ level
  1892. ; Out:
  1893. ;  ES:EBX = previous IRQ handler address
  1894. ;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
  1895. irqsetvect:
  1896.         push    eax
  1897.         push    ecx
  1898.         push    edx
  1899.  
  1900. ; get the PIC interrupt master and slave base address
  1901.  
  1902.         push    ebx
  1903.         push    ecx
  1904.         mov     ax,0400h
  1905.         int     31h
  1906.         pop     ecx
  1907.         pop     ebx
  1908.  
  1909. ; get the IDT interrupt slot number for the IRQ number
  1910.  
  1911.         mov     al,cl
  1912.         cmp     al,08h
  1913.         jb      irqsetvectf0
  1914.         mov     dh,dl
  1915.         sub     al,08h
  1916. irqsetvectf0:
  1917.         add     al,dh
  1918.  
  1919. ; saves and change the IRQ handler routine
  1920.  
  1921.         push    ds
  1922.         push    es
  1923.         push    ebx
  1924.         mov     ah,35h
  1925.         int     21h
  1926.         pop     edx
  1927.         pop     ds
  1928.         push    es
  1929.         push    ebx
  1930.         mov     ah,25h
  1931.         int     21h
  1932.         pop     ebx
  1933.         pop     es
  1934.         pop     ds
  1935.  
  1936.         pop     edx
  1937.         pop     ecx
  1938.         pop     eax
  1939.         ret
  1940.  
  1941. ;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
  1942. ; irqsetmask - enable or disable the IRQ in the interrupt mask registers
  1943. ; In:
  1944. ;  CL = IRQ level
  1945. ;  CH = enable (=0) or disable (=1)
  1946. ;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
  1947. irqsetmask:
  1948.         push    eax
  1949.         push    edx
  1950.  
  1951.         in      al,0A1h                 ; enable or disable the specified
  1952.         mov     ah,al                   ; IRQ using the PIC interrupt
  1953.         in      al,21h                  ; mask registers
  1954.         mov     dx,1
  1955.         shl     dx,cl
  1956.         not     dx
  1957.         and     ax,dx
  1958.         mov     dl,ch
  1959.         shl     dx,cl
  1960.         or      ax,dx
  1961.         out     21h,al
  1962.         mov     al,ah
  1963.         out     0A1h,al
  1964.  
  1965.         pop     edx
  1966.         pop     eax
  1967.         ret
  1968.  
  1969. ;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
  1970. ; irqsetup - install the IRQ handler routine
  1971. ;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
  1972. irqsetup:
  1973.         push    eax
  1974.         push    ebx
  1975.         push    ecx
  1976.  
  1977. ; setup DS data selector used by the interrupt handler
  1978.  
  1979.         mov     [datasel],ds
  1980.  
  1981. ; set the IRQ handler routine and saves the previous vector
  1982.  
  1983.         push    es
  1984.         mov     ax,cs
  1985.         mov     es,ax
  1986.         lea     ebx,[irqhandler]
  1987.         mov     cl,[irqnum]
  1988.         call    irqsetvect
  1989.         mov     [oldirqoff],ebx
  1990.         mov     [oldirqsel],es
  1991.         pop     es
  1992.  
  1993. ; enable the IRQ signals in the PIC interrupt mask
  1994.  
  1995.         mov     cl,[irqnum]
  1996.         mov     ch,0
  1997.         call    irqsetmask
  1998.  
  1999.         pop     ecx
  2000.         pop     ebx
  2001.         pop     eax
  2002.         ret
  2003.  
  2004. ;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
  2005. ; irqdone - restores the old IRQ handler routine
  2006. ;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
  2007. irqdone:
  2008.         push    ebx
  2009.         push    ecx
  2010.  
  2011. ; disable IRQ signals in the PIC interrupt mask register
  2012.  
  2013.         mov     cl,[irqnum]
  2014.         mov     ch,1
  2015.         call    irqsetmask
  2016.  
  2017. ; restore the old IRQ handler routine
  2018.  
  2019.         push    es
  2020.         mov     ebx,[oldirqoff]
  2021.         mov     es,[oldirqsel]
  2022.         mov     cl,[irqnum]
  2023.         call    irqsetvect
  2024.         pop     es
  2025.  
  2026.         pop     ecx
  2027.         pop     ebx
  2028.         ret
  2029.  
  2030. ;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
  2031. ; irqhandler - hardware IRQ handler routine
  2032. ;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
  2033. irqhandler:
  2034.         push    eax
  2035.         push    edx
  2036.         push    ds
  2037.         mov     ds,[cs:datasel]
  2038.  
  2039. ; send acknowledge to the PIC controller
  2040.  
  2041.         mov     al,20h
  2042.         cmp     [irqnum],08h
  2043.         jb      irqhandlerf0
  2044.         out     0A0h,al
  2045. irqhandlerf0:
  2046.         out     20h,al
  2047.  
  2048. ; send acknowledge to the DSP chip reading the 8 bit ack port (2XEh)
  2049.  
  2050.         mov     dx,[ioaddr]
  2051.         add     dx,0Eh
  2052.         in      al,dx
  2053.  
  2054. ; restart the 8 bit DMA mode playback transfer
  2055.  
  2056.         mov     al,14h
  2057.         call    sbwrite
  2058.         mov     al,0FFh
  2059.         call    sbwrite
  2060.         call    sbwrite
  2061.  
  2062.         pop     ds
  2063.         pop     edx
  2064.         pop     eax
  2065.         iretd
  2066.  
  2067. ;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
  2068. ; irqtest - testing hardware IRQ handler routine
  2069. ;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
  2070. irqtest:
  2071.         push    edx                     ; common IRQ test handler code
  2072.         push    ds                      ; used for autodetection
  2073.         mov     ds,[cs:datasel]
  2074.         mov     [irqnum],al             ; save the IRQ level number
  2075.         mov     dx,[ioaddr]             ; send acknowledge signal to the
  2076.         add     dx,0Eh                  ; DSP reading the ack port (2XEh)
  2077.         in      al,dx
  2078.         mov     al,20h                  ; send acknowledge to the PIC
  2079.         cmp     [irqnum],08h            ; controllers
  2080.         jb      irqtestf0
  2081.         out     0A0h,al
  2082. irqtestf0:
  2083.         out     20h,al
  2084.         pop     ds
  2085.         pop     edx
  2086.         pop     eax
  2087.         iretd
  2088.  
  2089.         irp     I,<2,3,5,7,10>          ; IRQ test handlers for each
  2090. irqtest&I:                              ; possible IRQ levels
  2091.         push    eax
  2092.         mov     ax,I
  2093.         jmp     irqtest
  2094.         endm
  2095.  
  2096. end
  2097.